package com.michale.framework.security.service.impl;

import com.michale.common.constants.CommonConstant;
import com.michale.common.constants.RedisCacheConstant;
import com.michale.common.domain.permissions.UserEntity;
import com.michale.common.exception.ServiceException;
import com.michale.common.exception.user.UserPasswordMaxNumberExceptions;
import com.michale.common.exception.user.UserPasswordNotMatchException;
import com.michale.common.properties.PassWordProperties;
import com.michale.common.utils.i18n.MessageUtils;
import com.michale.common.utils.redis.RedisUtils;
import com.michale.common.utils.security.SecurityUtils;
import com.michale.framework.config.async.asyncFactory.AsyncTask;
import com.michale.framework.config.async.manager.AsyncManager;
import com.michale.framework.security.context.AuthenticationContext;
import com.michale.framework.security.service.PasswordService;
import com.michale.system.service.LoginInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author Michale
 * @apiNote 验证密码
 */
@Slf4j
@Service("passwordService")
public class PasswordServiceImpl implements PasswordService {

    @Resource
    private RedisUtils redisUtils;
    @Resource
    private PassWordProperties passWordProperties;
    @Resource
    private LoginInfoService loginInfoService;

    /**
     * 登录密码方法
     *
     * @param userEntity 通过用户名查询到的用户信息
     */
    @Override
    public void validate(UserEntity userEntity) {
        //从上下文中获取Authentication
        Authentication authentication = AuthenticationContext.getAuthentication();
        //从Authentication中获取用户输入的信息
        String inUserName = authentication.getName();
        String inPassWord = authentication.getCredentials().toString();

        //从缓存中查询密码错误输入次数
        Integer retryCount = getRetryCount(inUserName);
        if (retryCount == null) {
            retryCount = 0;
        }
        //判断次数是否大于密码最大错误次数
        if (retryCount >= passWordProperties.getMaxRetryCount()) {
            //抛出密码过长异常
            AsyncManager.asyncManager().start(AsyncTask.recordLoginInfo(inUserName, CommonConstant.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", passWordProperties.getMaxRetryCount(), passWordProperties.getLockTime())));
            throw new UserPasswordMaxNumberExceptions(passWordProperties.getMaxRetryCount(), passWordProperties.getLockTime());
        } else if (!isCheckPassword(userEntity, inPassWord)) { //进行密码比对
            retryCount = retryCount + 1;
            //比对失败记录输入次数
            AsyncManager.asyncManager().start(AsyncTask.recordLoginInfo(inUserName, CommonConstant.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount)));
            //将输入次数缓存到redis中
            cacheToRedis(inUserName, retryCount);
            throw new UserPasswordNotMatchException();
        } else {
            //登录成功之后清空记录
            clearLoginRecordCache(inUserName);
        }
    }

    /**
     * 登录账户密码错误次数缓存键名
     *
     * @param userName 用户名
     * @return 缓存键key
     */
    private String getCacheKey(String userName) {
        return RedisCacheConstant.PWD_ERR_CNT_KEY + userName;
    }

    /**
     * 查询密码错误输入次数
     *
     * @param inUserName 登录时输入的账户名
     * @return 密码错误输入次数
     */
    private Integer getRetryCount(String inUserName) {
        return redisUtils.getCacheObject(getCacheKey(inUserName));
    }

    /**
     * 将输入次数缓存到redis中
     *
     * @param userName   登录时输入的账户名
     * @param retryCount 记录错误次数
     */
    private void cacheToRedis(String userName, Integer retryCount) {
        redisUtils.setCacheObject(getCacheKey(userName), retryCount, passWordProperties.getLockTime(), TimeUnit.MINUTES);
    }

    /**
     * 进行密码比对
     *
     * @param sysUser    通过用户名查询到的账户信息
     * @param inPassWord 用户登录时输入的信息
     * @return 比对结果
     */
    private boolean isCheckPassword(UserEntity sysUser, String inPassWord) {
        return SecurityUtils.matchesPassword(inPassWord, sysUser.getPassword());
    }

    /**
     * 登录成功清空缓存
     *
     * @param userName 账户名
     */
    public void clearLoginRecordCache(String userName) {
        //清空redis中的缓存
        if (redisUtils.hasKey(getCacheKey(userName))) {
            redisUtils.deleteObject(getCacheKey(userName));
        }
    }

    /**
     * 账户解锁
     *
     * @param userName 带解锁的账户名
     */
    @Override
    @Transactional
    public void unlock(String userName) {
        //清空redis中的缓存
        if (redisUtils.hasKey(getCacheKey(userName))) {
            if (!redisUtils.deleteObject(getCacheKey(userName))) {
                throw new ServiceException("账户解锁", "解锁失败");
            }
            //清空登录日志
            loginInfoService.deleteLoginInfoByUserName(userName);
        }
    }
}
